﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;

namespace KsWare.Breconium.Core {

	public class BreconiumServer {

		#region Static
		private static BreconiumServer sDefault;

		static BreconiumServer() {
			
			sDefault = new BreconiumServer();
		}

		public static BreconiumServer Default {get {return sDefault;} private set {sDefault = value;}}

		#endregion

		private HttpListener mListener;
		private Thread mListenerThread;
		private readonly List<Worker> mWorkers;
		private readonly List<Worker> mWorkersFinished;

		private Dictionary<string, Session> mSessions;
		private Session mDefaultSession;
		private volatile ServerState mState;

		public BreconiumServer () {
			mSessions        = new Dictionary<string, Session>();
			mWorkers         = new List<Worker>();
			mWorkersFinished = new List<Worker>();
		}

		public Dictionary<string, Session> Sessions {get {return mSessions;}set {mSessions = value;}}
		public Session DefaultSession { get { return mDefaultSession; } }

		public void Start(string[] prefixes) {
			if (!HttpListener.IsSupported) {
				Console.WriteLine ("Windows XP SP2 or Server 2003 is required to use the HttpListener class.");
				return;
			}
			// URI prefixes are required,
			// for example "http://contoso.com:8080/index/".
			if (prefixes == null || prefixes.Length == 0)
			  throw new ArgumentException("prefixes");

			mState=ServerState.StartRequested;

			// Create a listener.
			mListener = new HttpListener();
			// Add the prefixes.
			foreach (string s in prefixes) {
				mListener.Prefixes.Add(s);
			}
			mListener.Start();
			mListenerThread=new Thread(ListenerProc);
			mListenerThread.Start(mListener);
		}

		public void Stop() {
			mState=ServerState.StopRequested;
			mListener.Close();
			mListenerThread.Abort("stop server requested");
			foreach (var worker in mWorkers) {
				if(worker.State==WorkerState.Stopped) continue;
				worker.Stop();
			}
		}

		private void ListenerProc(object o) {
			mState=ServerState.Startet;
			var listener = (HttpListener) o;
			try {
				Console.WriteLine("Listening...");
				while (true) {
					// Note: The GetContext method blocks while waiting for a request. 
					HttpListenerContext context = listener.GetContext();
					var worker=new Worker(this, context);
					lock (mWorkers) {mWorkers.Add(worker);}
					worker.Start();
				}
			} catch(Exception ex){
				if(mState==ServerState.StopRequested) return;
				throw;
			} finally {
				try{listener.Stop();}catch{}
				Console.WriteLine("Not Listening");
			}
		}

		public void RegisterSession(Session session) { 
			Console.WriteLine("New Session startet. "+"\r\n\tLocation: "+session.Location);
			lock (mSessions) {mSessions.Add(session.Id,session);}
			mDefaultSession = session;
			OnSessionStart(session);
		}

		public event EventHandler<SessionEventArgs> SessionStart;

		private void OnSessionStart(Session session) {
			if(SessionStart==null) return;
			SessionStart(this,new SessionEventArgs(session));
		}

		internal void WorkerOnFinished(Worker worker) {
			lock (mWorkers) {mWorkers.Remove(worker);}
			#if(DEBUG)
			lock (mWorkersFinished) {mWorkersFinished.Add(worker);}
			#endif
		}
	}

	internal enum ServerState {
		None,
		StartRequested,
		Startet,
		StopRequested,
		Stopped,
	}

	public class SessionEventArgs:EventArgs {
		
		public SessionEventArgs(Session session) {
			Session = session;
		}

		public Session Session { get; private set; }
	}
}
